﻿using System.Xml;
using System.Xml.Serialization;

namespace ECommon
{
    /// <summary>
    /// xml工具
    /// </summary>
    public static class XmlTool
    {
        /// <summary>
        /// 直接根据xml的结点路径获取结点值
        /// </summary>
        /// <param name="xpath">xml结点路径</param>
        /// <param name="xmlFilePath">xml文件路径</param>
        /// <returns>node value string</returns>
        /// <exception cref="ArgumentException">变量错误异常</exception>
        private static string GetValueByXPath(string xpath, string xmlFilePath)
        {
            string res = string.Empty;
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(xmlFilePath);
            if (xmlDoc == null)
            {
                xmlDoc = new XmlDocument();
                xmlDoc.LoadXml(xmlFilePath);
            }
            if (xmlDoc == null)
            {
                throw new ArgumentException("xml文件或文件路径参数出错");
            }
            else
            {
                XmlNode? node = xmlDoc.SelectSingleNode(xpath);
                if (node == null)
                {
                    throw new ArgumentException("xml结点或结点路径参数出错");
                }
                else
                {
                    string[] tmp = node.InnerText.Split(new char[] { '\t', '\r', '\n', ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    if (tmp != null && tmp.Length > 0)
                    {
                        res = string.Join(" ", tmp);
                    }
                }

            }
            if (string.IsNullOrWhiteSpace(res))
            {
                throw new ArgumentException("xml结点或结点路径参数出错");
            }
            else return res;

        }

        /// <summary>
        /// 直接根据xml的结点路径设置结点值
        /// </summary>
        /// <param name="xpath">xml结点路径</param>
        /// <param name="xmlFilePath">xml文件路径</param>
        /// <param name="nodeValue">xml节点值</param>
        /// <returns>设置结果</returns>
        private static bool SetValueByXPath(string xpath, string xmlFilePath, string nodeValue)
        {
            bool res = false;
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(xmlFilePath);
            if (xmlDoc == null)
            {
                return res;
            }
            XmlNode? node = xmlDoc.SelectSingleNode(xpath);
            if (node == null)
            {
                return res;
            }
            node.InnerText = nodeValue;
            res = true;

            xmlDoc.Save(xmlFilePath);
            return res;
        }

        /// <summary>
        /// 根据xml的结点路径获取结点值
        /// </summary>
        /// <param name="xpath">结点路径</param>
        /// <param name="xmlFilePath">xml文件路径</param>
        /// <param name="isUsedCache">是否使用缓存</param>
        /// <returns>结点值</returns>
        /// <exception cref="ArgumentNullException">参数为空异常</exception>
        /// <exception cref="FileNotFoundException">文件不存在异常</exception>
        public static string GetNodeValueByXPath(string xpath, string xmlFilePath = "", bool isUsedCache = false)
        {
            if (string.IsNullOrWhiteSpace(xpath))
            {
                throw new ArgumentNullException("xpath", "节点路径为空");
            }
            xmlFilePath = xmlFilePath.Replace('\\', Path.DirectorySeparatorChar);
            if (string.IsNullOrWhiteSpace(xmlFilePath))
            {
                throw new ArgumentNullException("xmlFilePath", "xml文件路径为空");
            }
            if (!File.Exists(xmlFilePath))
            {
                throw new FileNotFoundException("xml文件不存在");
            }
            string res = string.Empty;
            if (isUsedCache)
            {
                string cacheKey = (xpath + xmlFilePath).GetHashCode().ToString();
                res = CacheTool.Get<string>(cacheKey);
                if (string.IsNullOrWhiteSpace(res))
                {
                    res = GetValueByXPath(xpath, xmlFilePath);
                    CacheTool.Set(cacheKey, res, 30);
                }
            }
            else
            {
                res = GetValueByXPath(xpath, xmlFilePath);
            }
            return res;
        }

        /// <summary>
        /// 根据xml的结点路径设置结点值
        /// </summary>
        /// <param name="xpath">结点路径</param>
        /// <param name="nodeValue">结点值</param>
        /// <param name="isUsedCache">是否使用缓存</param>
        /// <param name="xmlFilePath">xml文件路径</param>
        /// <param name="isUsedCache">是否使用缓存</param>
        public static bool SetNodeValueByXPath(string xpath, string nodeValue, string xmlFilePath = "", bool isUsedCache = true)
        {
            if (string.IsNullOrWhiteSpace(xpath))
            {
                throw new ArgumentNullException("xpath", "节点路径为空");
            }
            if (string.IsNullOrWhiteSpace(xmlFilePath))
            {
                throw new ArgumentNullException("xmlFilePath", "xml文件路径为空");
            }
            xmlFilePath = xmlFilePath.Replace('\\', Path.DirectorySeparatorChar);
            if (!File.Exists(xmlFilePath))
            {
                throw new FileNotFoundException("xml文件不存在");
            }
            if (isUsedCache)
            {
                string cacheKey = (xpath + xmlFilePath).GetHashCode().ToString();
                CacheTool.Set(cacheKey, nodeValue, 30);
            }
            return SetValueByXPath(xpath, xmlFilePath, nodeValue);
        }

        /// <summary>
        /// xml反序列化
        /// </summary>
        /// <typeparam name="T">要反序列化的类型</typeparam>
        /// <param name="xmlPath">xml文档存储路径</param>
        /// <returns>结果</returns>
        public static T? XmlDeserialize<T>(string xmlPath)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            T? res = default(T);
            if (!File.Exists(xmlPath))
            {
                return res;
            }
            using (FileStream fsRead = File.OpenRead(xmlPath))
            {
                object? obj = serializer.Deserialize(fsRead);
                if (obj != null)
                {
                    res = (T)obj;
                }
            }
            return res;
        }


        /// <summary>
        /// xml序列化
        /// </summary>
        /// <param name="obj">对象的值</param>
        /// <param name="xmlPath">xml文档存储路径</param>
        public static void XmlSerialize(object obj, string xmlPath)
        {
            if (obj == null)
            {
                return;
            }
            XmlSerializer serializer = new XmlSerializer(obj.GetType());
            if (!File.Exists(xmlPath))
            {
                IoTool.CreateDirectory(xmlPath);
                using (FileStream fs = File.Create(xmlPath))
                {
                    serializer.Serialize(fs, obj);
                }
            }
            else
            {
                using (FileStream fsWrite = File.OpenWrite(xmlPath))
                {
                    serializer.Serialize(fsWrite, obj);
                }
            }
        }
    }
}
